import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
<<<<<<< HEAD
import java.util.List;
=======
import java.util.ArrayList;
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58


/**
 * @author Robert C. Duvall, revised by Yaqi Zhang
 */
public class Mass implements Material {
    // state
    private static final int MASS_SIZE = 16;
<<<<<<< HEAD
    private static final int WALL_ID = 3;
    private static final double WR_MAGNITUDE = 1;
    private static final double WR_EXPONENT = 2;
=======
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58

    private Point2D myCenter;
    private Force myVelocity;
    private Dimension mySize;
    private int myID;
    private double myMass;
    private Force myAcceleration;
    private ArrayList<ApplyForce> applicableForce;

    /**
     * @param id id of a mass
     * @param x x coordinate of a mass
     * @param y y coordinate of a mass
     * @param mass mass of a mass
     */
    public Mass (int id, double x, double y, double mass) {
        myAcceleration = new Force();
        myMass = mass;
        myID = id;
        setCenter(x, y);
        setVelocity(0, 0);
        mySize = new Dimension(MASS_SIZE, MASS_SIZE);
        
    }

    /**
     * implements Material.paint.
     * 
     * @param pen pen to draw the mass
     */
    @Override
    public void paint (Graphics2D pen) {
        pen.setColor(Color.BLACK);
        pen.fillOval(getLeft(), getTop(), getSize().width, getSize().height);
    }

    /**
     * implements Material.update.
     * 
     * @param canvas canvas to draw at
     * @param dt time interval to refresh the screen
     */
    @Override
    public void update (Simulation canvas, double dt) {
        updateVelocity(canvas);
        // move mass by velocity
        myCenter.setLocation(myCenter.getX() + myVelocity.getXChange() * dt,
                myCenter.getY() + myVelocity.getYChange() * dt);
    }

<<<<<<< HEAD
    protected void updateVelocity (Simulation canvas) {
        applyForce(getCenterOfMassForce(canvas.getRealMass(),this,100,1));
        //applyForce(new Gravity(this));
        //applyForce(getBounce(canvas.getSize()));
        //applyForce(new DragForce(this));
        //applyForce(getWallRepulse(canvas.getSize(), WALL_ID, WR_MAGNITUDE,
                //WR_EXPONENT));
=======
    protected void updateVelocity(Simulation canvas) {
        applyForce(getBounce(canvas.getSize()));
        applicableForce=Factory.getFactory().getApplicableForce();
        if(applicableForce.size()!=0){
            for(ApplyForce af: applicableForce){ 
                Force f=af.getForce(this);
                 applyForce(f);
        
            }
        }
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58
        // convert force back into Mover's velocity
        getVelocity().sum(myAcceleration);
        myAcceleration.reset();
    }

<<<<<<< HEAD
=======
    

>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58
    /**
     * @param f
     *        force to be applied to this mass
     */
    public void applyForce (Force f) {
        myAcceleration.sum(f);
    }

    /**
     * return the id of this mass.
     * 
     * @param id the id of the mass
     */
    public boolean match (int id) {
        return myID == id;
    }

<<<<<<< HEAD
=======
    // add gravity towards bottom
   
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58
    // check for move out of bounds
    private Force getBounce (Dimension bounds) {
        Force impulse = new Force();
        if (getLeft() < 0) {
            impulse = new Force(0, 2);
            setCenter(getSize().width / 2, getCenter().getY());
        }
        else if (getRight() > bounds.width) {
            impulse = new Force(180, 2);
            setCenter(bounds.width - getSize().width / 2, getCenter().getY());
        }
        if (getTop() < 0) {
            impulse = new Force(270, 2);
            setCenter(getCenter().getX(), getSize().height / 2);
        }
        else if (getBottom() > bounds.height) {
            impulse = new Force(90, 2);
            setCenter(getCenter().getX(), bounds.height - getSize().height / 2);
        }
        impulse.scale(getVelocity().getRelativeMagnitude(impulse));
        return impulse;
    }
<<<<<<< HEAD

    private Force getWallRepulse (Dimension bounds, int wallID,
            double maxMagnitude, double exponent) {
        double distance;
        double direction;
        if (wallID == 1) {
            // top
            distance = getCenter().getY();
            direction = 90;
        }
        else if (wallID == 2) {
            // right
            distance = bounds.getWidth() - getCenter().getX();
            direction = 180;
        }
        else if (wallID == 3) {
            // bottom
            distance = bounds.getHeight() - getCenter().getY();
            direction = 270;
        }
        else {
            // left
            distance = getCenter().getX();
            direction = 0;
        }
        double magnitude = maxMagnitude / Math.pow(distance, exponent);
        // System.out.println(distance);
        Force repulse = new Force(direction, magnitude);
        return repulse;
    }
=======
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58

    public Force getCenterOfMassForce (List<Mass> realMass, Mass mass, double magnitude,
            double exponent) {
        Point2D centerOfMass = getCenterOfMass(realMass);
        double centerOfMassX = centerOfMass.getX();
        double centerOfMassY = centerOfMass.getY();
        double massX = mass.getCenter().getX();
        double massY = mass.getCenter().getY();
        double distance = Point2D.distance(centerOfMassX, centerOfMassY, massX, massY);
        double forceMagnitude = magnitude / Math.pow(distance, exponent);
        System.out.println(forceMagnitude);
        double dx = centerOfMassX - massX;
        double dy = centerOfMassY - massY;
        double angle = Force.angleBetween(dx, dy);
        return new Force(angle,forceMagnitude);
    }

    public Point2D getCenterOfMass (List<Mass> realMass) {
        double cmX = 0;
        double cmY = 0;
        double sumMass = 0;
        for (Mass m : realMass) {
            Point2D center = m.getCenter();
            double mass = m.getMass();
            sumMass += mass;
            cmX += mass * center.getX();
            cmY += mass * center.getY();
        }
        cmX = cmX/sumMass;
        cmY = cmY/sumMass;
        return new Point2D.Double(cmX,cmY);
    }

    /**
     * Returns shape's velocity.
     */
    public Force getVelocity () {
        return myVelocity;
    }

    /**
     * Resets shape's velocity.
     * 
     * @param direction the direction of the velocity
     * @param magnitude the magnitude of the velocity
     */
    public void setVelocity (double direction, double magnitude) {
        myVelocity = new Force(direction, magnitude);
    }

    /**
     * Returns shape's center.
     */
    public Point2D getCenter () {
        return myCenter;
    }

    /**
     * Resets shape's center.
     * 
     * @param x the x coordinate of the center
     * @param y the y coordinate of the center
     */
    public void setCenter (double x, double y) {
        myCenter = new Point2D.Double(x, y);
    }

    /**
     * Returns shape's left-most coordinate.
     */
    public int getLeft () {
        return (int) (getCenter().getX() - getSize().width / 2);
    }

    /**
     * Returns shape's top-most coordinate.
     */
    public int getTop () {
        return (int) (getCenter().getY() - getSize().height / 2);
    }

    /**
     * Returns shape's right-most coordinate.
     */
    public int getRight () {
        return (int) (getCenter().getX() + getSize().width / 2);
    }

    /**
     * Reports shape's bottom-most coordinate.
     * 
     * @return bottom-most coordinate
     */
    public int getBottom () {
        return (int) (getCenter().getY() + getSize().height / 2);
    }

    /**
     * Returns shape's size.
     */
    public Dimension getSize () {
        return mySize;
    }

    /**
     * Returns the acceleration of this mass.
     */
    public Force getAcceleration () {
        return myAcceleration;
    }

    /**
     * Returns the id of this mass.
     */
    @Override
    public int getId () {
        return myID;
    }
<<<<<<< HEAD

    public double getMass () {
        return myMass;
    }
=======
    
    
    public double getMass () {
        return myMass;
    }
    
    
>>>>>>> db3a22b30e69cbc14bd7f2fe6aa88415016cfe58
}
